#只有粒球的分类划分功能

import numpy as np
import warnings
from sklearn.cluster import k_means
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
from itertools import combinations

warnings.filterwarnings("ignore")

GBi_dict = {}

def GBi_number(idx1, point_to_gb, attr_name=None):
    global GBi_dict
    if attr_name is None:
        attr_name = f"a{idx1 + 1}"
    unique_labels = np.unique(point_to_gb)
    for i, label in enumerate(unique_labels):
        indices = np.where(point_to_gb == label)[0]
        GBi_dict[f"GB[{attr_name},{i + 1}]"] = set(f"x{idx+1}" for idx in indices)
    print(f"存储 point_to_gb 的索引到 GBi_number 函数中，属性 {attr_name}: {point_to_gb}")

def calculate_center_and_radius(gb):
    center = gb.mean(axis=0)
    radius = np.max(np.linalg.norm(gb - center, axis=1))
    return center, radius

def splits(gb_list, num, k):
    new_list = []
    for gb in gb_list:
        if gb.shape[0] < num:
            new_list.append(gb)
        else:
            new_list.extend(splits_ball(gb, k))
    return new_list

def splits_ball(gb, k):
    pts = np.unique(gb, axis=0)
    if pts.shape[0] < k:
        k = pts.shape[0]
    labels = k_means(X=gb, n_clusters=k, n_init=1, random_state=8)[1]
    return [gb[labels == lab] for lab in range(k)]

def assign_points_to_closest_gb(data, gb_centers):
    assigned = np.zeros(data.shape[0], dtype=int)
    for idx, sample in enumerate(data):
        assigned[idx] = np.argmin(np.linalg.norm(sample - gb_centers, axis=1))
    return assigned

def fuzzy_similarity(t_data, sigma=0, k=2):
    t_n = t_data.shape[0]
    gb_list = [t_data]
    num = np.ceil(np.sqrt(t_n))
    while True:
        before = len(gb_list)
        gb_list = splits(gb_list, num=num, k=k)
        if len(gb_list) == before:
            break
    centers = np.array([calculate_center_and_radius(gb)[0] for gb in gb_list])
    return assign_points_to_closest_gb(t_data, centers)

def GBFRD(data, sigma=0):
    n, m = data.shape
    
    # 枚举所有非空子集：1 到 (1<<m)-1
    for mask in range(1, 1 << m):
        # 找到当前子集对应的列索引
        idxs = [j for j in range(m) if (mask >> j) & 1]
        
        # 子集名称： e.g. [0,2] -> "a1a3"
        attr_name = "".join(f"a{j+1}" for j in idxs)
        
        # 按子集做球状模糊划分
        pt2gb = fuzzy_similarity(data[:, idxs], sigma, k=2)
        
        # 存储结果
        # 第一个参数 idx1 不再代表“单属性”，直接填 0（不影响 attr_name）
        GBi_number(0, pt2gb, attr_name)
    
    return 0


if __name__ == '__main__':
    data = pd.read_csv("Code/guiyihua_test.csv").values
    mask = (data >= 1).all(axis=0) & (data.max(axis=0) != data.min(axis=0))
    if mask.any():
        data[:, mask] = MinMaxScaler().fit_transform(data[:, mask])
    sigma = 0.5
    GBFRD(data, sigma)
    
    print("\n最终存储的 GBi_dict:")
    for key, val in GBi_dict.items():
        print(f"{key} = {val}")
